In [1]:
import plotly.graph_objects as go
import pandas as pd
import numpy as np
import os
import sys

# Lectura del dataframe
url = 'https://raw.githubusercontent.com/iruiper/Cyberattacks-History/master/data/01_clean/EstadisticasAtaques2017_2020_Input.csv'
df = pd.read_csv(url, sep=';', decimal=',', encoding='utf-8')

# Suprimimos los target class que sean X,Y o Z y los continentes desconocidos.
cols_plot = ['Continent', 'NumeroAtaques', 'Code_target_class', 'Desc_target_class']
df_plot = df[(df.Continent != 'Desconocido') & ~(df.Code_target_class.isin(['X', 'Y', 'Z']))][cols_plot]

# Creamos una variable adicional que segregará entre ataques internacionales y unicontinentales
df_plot.loc[:, 'Kind'] = np.where(df_plot.Continent == 'International', '<b>International</b>', '<b>Single   <br>Continent</b>')

# Datos para generar el gráfico general del número de ataques internacionales vs single continent
df_plot_general = df_plot[['Kind', 'NumeroAtaques']].groupby('Kind', as_index=False).sum()

# Datos para generar el gráfico general de número de ataques internacionales vs single continent para public class
df_plot_general_public = df_plot[df_plot.Code_target_class=='O'][['Kind', 'NumeroAtaques']].groupby('Kind', as_index=False).sum()
In [11]:
fig = go.Figure()

# Se añaden en el gráfico los ataques totales
fig.add_trace(
    go.Bar(
        x=df_plot_general.NumeroAtaques,
        y=df_plot_general.Kind,
        text=df_plot_general.NumeroAtaques,
        textposition='outside',
        orientation='h',
        marker=dict(color='rgba(130,201,241,0.4)'),
        hoverinfo='skip',
        name='Todas las empresas',
        cliponaxis=False
    )
)

# Se añaden en el gráfico los ataques para el sector público
fig.add_trace(
    go.Bar(
        x=df_plot_general_public.NumeroAtaques,
        y=df_plot_general_public.Kind,
        text=df_plot_general_public.NumeroAtaques,
        textposition='outside',
        orientation='h',
        marker=dict(color='rgba(14,76,112,0.9)'),
        hoverinfo='skip',
        name='Empresas sector público'
    )
)

# Se modifican algunas características visuales del gráfico como axes, legend, etc.
fig.update_layout(
    barmode='overlay',
    title=dict(
        text = f'<b>Distribución ataques<br>internacionales vs continentales</b>',
        x = 0.5,
        xanchor = 'center',
        yanchor = 'top'),
    titlefont=dict(size=18),
    plot_bgcolor='white',
    xaxis=dict(showgrid=False, visible=False, zeroline=False, autorange=True, automargin=True),
    yaxis=dict(showgrid=False, visible=True, zeroline=False, autorange=True),
    showlegend=True,
    legend_orientation='h'
)

fig.show('notebook')
In [4]:
# Se generan los datos para el gráfico de barras de los ataques totales en función del continente. Primero
# se descartan aquellos ataques internacionales.
df_continent = df_plot[df_plot.Continent != 'International']

# Se agrupa cada continente y se suman los ataques.
df_continent_total = df_continent[['Continent', 'NumeroAtaques']].groupby('Continent', as_index=False).sum()

# Se ordenan los datos para mejorar facilitar las comparaciones entre elementos del gráfico.
df_continent_total.sort_values('NumeroAtaques', ascending=True, inplace=True)
df_continent_total
Out[4]:
Continent NumeroAtaques
4 África 23
2 Australia y Oceanía 44
1 Asia 225
3 Europa 374
0 América 1207
In [5]:
# Se generan los datos para el gráfico de barras de los ataques totales en función del continente. Se seleccionan
# únicamente los ataques dirigidos al sector público.
df_continent_total_public = df_continent[df_continent.Code_target_class == 'O'][['Continent', 'NumeroAtaques']].groupby('Continent', as_index=False).sum()

# Se ordenan los datos para mejorar facilitar las comparaciones entre elementos del gráfico.
df_continent_total_public.sort_values('NumeroAtaques', ascending=True, inplace=True)
df_continent_total_public
Out[5]:
Continent NumeroAtaques
4 África 7
2 Australia y Oceanía 8
1 Asia 64
3 Europa 87
0 América 225
In [12]:
fig = go.Figure()

# Se añaden en el gráfico los ataques totales por continente
fig.add_trace(
    go.Bar(
        x=df_continent_total.NumeroAtaques,
        y=df_continent_total.Continent,
        text=df_continent_total.NumeroAtaques,
        textposition='outside',
        orientation='h',
        marker=dict(color='rgba(130,201,241,0.4)'),
        hoverinfo='skip',
        name='Todas las empresas',
        cliponaxis=False
    )
)

# Se añaden en el gráfico los ataques totales por continente para empresas del sector público
fig.add_trace(
    go.Bar(
        x=df_continent_total_public.NumeroAtaques,
        y=df_continent_total_public.Continent,
        text=df_continent_total_public.NumeroAtaques,
        textposition='inside',
        orientation='h',
        marker=dict(color='rgba(14,76,112,0.9)'),
        hoverinfo='skip',
        name='Empresas sector público'
    )
)

# Modificaciones estéticas del gráfico.
fig.update_layout(
    barmode='overlay',
    title=dict(
        text = f'<b>Distribución ataques<br>continentales</b>',
        x = 0.5,
        xanchor = 'center',
        yanchor = 'top'),
    titlefont=dict(size=18),
    plot_bgcolor='white',
    xaxis=dict(showgrid=False, visible=False, zeroline=False, autorange=True, automargin=True),
    yaxis=dict(showgrid=False, visible=True, zeroline=False, autorange=True),
    showlegend=True,
    legend_orientation='h'
)

fig.show('notebook')
In [13]:
fig = go.Figure()

# Se normalizan los ataques para el sector público en función de los ataques totales en cada continente.
df_norm = df_continent_total_public.copy()
df_norm.set_index('Continent', inplace=True, drop=False)
df_norm.loc[:, 'Total'] = df_continent_total.set_index('Continent').NumeroAtaques
df_norm.loc[:, 'Norm'] = df_norm.NumeroAtaques / df_norm.Total
df_norm.loc[:, 'Txt'] = df_norm.Norm.apply(lambda x: str(round(x*100,2)) + '%')
df_norm.sort_values('Norm', ascending=True, inplace=True)
df_norm

# Gráfico de barras con los ataques normalizdos
fig.add_trace(
    go.Bar(
        x=df_norm.Norm,
        y=df_norm.Continent,
        text=df_norm.Txt,
        textposition='inside',
        orientation='h',
        marker=dict(color='rgba(14,76,112,0.9)'),
        hoverinfo='skip',
        name='Empresas sector público'
    )
)

# modificaciones estéticas del gráfico
fig.update_layout(
    barmode='overlay',
    title=dict(
        text = f'<b>Distribución normalizada de ataques<br>continentales</b>',
        x = 0.5,
        xanchor = 'center',
        yanchor = 'top'),
    titlefont=dict(size=18),
    plot_bgcolor='white',
    xaxis=dict(showgrid=False, visible=False, zeroline=False, autorange=True, automargin=True),
    yaxis=dict(showgrid=False, visible=True, zeroline=False, autorange=True),
    showlegend=True,
    legend_orientation='h'
)

fig.show('notebook')
In [9]:
# Diccionario que permitirá ajustar los labels dentro del gráfico
adjust_label = {
    'Public administration and defence, compulsory social security': 'Public administration<br>and defence, compulsory<br>social security',
    'Financial and insurance activities': 'Financial and<br>insurance activities',
    'Information and communication': 'Information and<br>communication',
    'Arts entertainment and recreation': 'Arts entertainment<br>and recreation',
    'Human health and social work activities': 'Human health and<br>social work activities'
}

# Nos quedamos exclusivamente con los ataques producidos en europa
df_europa = df_plot[df_plot.Continent == 'Europa']

# Agregamos el número de ataques en función del sector al que afectan.
df_europa_total = df_europa[['Code_target_class', 'Desc_target_class', 'NumeroAtaques']].groupby(['Code_target_class','Desc_target_class'], as_index=False).sum()

# Se ordenan los datos para mejorar facilitar las comparaciones entre elementos del gráfico.
df_europa_total.sort_values('NumeroAtaques', ascending=True, inplace=True)

# Asignamos un color distinto a los ataques del sector publico para facilitar la identificación de elementos en el gráfico
df_europa_total.loc[:, 'color'] = np.where(df_europa_total['Code_target_class'] == 'O',
                                           'rgba(14,76,112,0.9)',
                                           'rgba(198,202,204,1)')

# Nos quedamos con el top 5 de sectores atacados. Como el df esta ordenado en forma ascendente, el top 5 se encuentra 
# en las últimas 5 posiciones
df_europa_plot = df_europa_total.tail(5)

# Ajustamos las etiquetas a través del diccionario
df_europa_plot.Desc_target_class = df_europa_plot.Desc_target_class.map(adjust_label)
C:\Users\USUARIO\Anaconda3\envs\data.viz\lib\site-packages\pandas\core\generic.py:5303: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

In [14]:
fig = go.Figure()

# Generación del grafico de barras con el top 5 de empresas europeas atacadas
fig.add_trace(
    go.Bar(
        x=df_europa_plot.NumeroAtaques,
        y=df_europa_plot.Desc_target_class,
        text=df_europa_plot.NumeroAtaques,
        textposition='outside',
        orientation='h',
        marker=dict(color=df_europa_plot.color),
        hoverinfo='skip',
        name='Todas las empresas',
        cliponaxis=False
    )
)

# Modificaciones estéticas del gráfico.
fig.update_layout(
    barmode='overlay',
    title=dict(
        text = f'<b>Distribución ataques europeos<br>según el tipo de empresa</b>',
        x = 0.5,
        xanchor = 'center',
        yanchor = 'top'),
    titlefont=dict(size=18),
    plot_bgcolor='white',
    xaxis=dict(showgrid=False, visible=False, zeroline=False, autorange=True, automargin=True),
    yaxis=dict(showgrid=False, visible=True, zeroline=False, autorange=True, automargin=True),
    showlegend=False,
    legend_orientation='h',
    margin=dict(l=100)
)

fig.show('notebook')